package com.grt192.ui;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.Vector;

import javax.swing.JComponent;

import com.grt192.core.RobotModel;
import com.grt192.core.RobotModelListener;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;

/**
 * A display component that shows the current location and past locations of the robot,
 * and takes input from the user and then tells the robot model what to do.
 * @author Nikhil, Vikram, Cody, and Anand
 *
 */
public class MapComponent extends JComponent implements MouseListener,
        MouseMotionListener, RobotModelListener, KeyListener {

    public static final double X_RANGE = 1.0;
    public static final double Y_RANGE = 1.0;
    // TODO calibrate DISTANCE_CONVERSION_FACTOR
    public static final double DISTANCE_CONVERSION_FACTOR = 1.0;
    public static final int CRUMB_POINT_SIZE = 10;
    private static final int ROWS = 600 / 50; //number of rows in the grid
    private static final int COLUMNS = 800 / 50; //number of columns in the grid
//      public static final int LOCATION_POINT_SIZE = 20;
    private RobotModel model;
    private Image CommandWindow,
            CursorSelect;
    //CommandSelector;
    private ImageSet RobotIcon;
    private Vector<RealPoint> pointHistory;
    // TODO calibrate MODEL_SPEED
    private static final double MODEL_SPEED = 1.0;
    private boolean commandWindowActive = false;
    private int commandWindowX, commandWindowY;
    private int lastX, lastY;
    private boolean select;
    private int cursorX, cursorY;
    private double rowChange;
    private double columnChange;
    private RealPoint currentDestination;

    public MapComponent(RobotModel model) {
        this.rowChange = 0;
        this.rowChange = 0;
        this.model = model;
        this.RobotIcon = new ImageSet();
        pointHistory = new Vector<RealPoint>();
        this.addMouseListener(this);
        this.addMouseMotionListener(this);
        pointHistory.add(new RealPoint(0d, 0d));
        model.addRobotModelListener(this);


        Toolkit kit = Toolkit.getDefaultToolkit();
        /*
        //loading each Image from the TriangleIcon set
        TriangleIconBlack = kit.getImage("TriangleIconBlack.png");
        TriangleIconGray = kit.getImage("TriangleIconGray.png");
        TriangleIconWhite = kit.getImage("TriangleIconWhite.png");
        TriangleIconRed = kit.getImage("TriangleIconRed.png");
        TriangleIconOrange = kit.getImage("TriangleIconOrange.png");
        TriangleIconYellow = kit.getImage("TriangleIconYellow.png");
        TriangleIconGreen = kit.getImage("TriangleIconGreen.png");
        TriangleIconBlue = kit.getImage("TriangleIconBlue.png");
        TriangleIconPurple = kit.getImage("TriangleIconPurple.png");
         */

        CommandWindow = kit.getImage("CommandWindowGradient.png");
        CursorSelect = kit.getImage("CursorSelect.png");
        //CommandSelector = kit.getImage("CommandWindowSelect.png");

        //Assigning the RobotIcon to be a member of the set
        RobotIcon.setImage(RobotIcon.BLACK);
    }

    /**
     * Paints everything on the screen that must be painted: Grid painted
     * as lines across screen. History of previous points are represented
     * as gray circles connected by lines. Robot drawn at the center, at
     * the heading from the Robot Model. Command Window drawn if active.
     * @param g the graphics system used
     */
    @Override
    public void paintComponent(Graphics g) {
        //Paint Grid With moving Grid
        int rowHeight = getHeight() / ROWS;
        for (int i = 0; i <= ROWS+1; i++) {
            double y = i * rowHeight + rowChange;
            g.drawLine(0, (int)y, getWidth(), (int)y);
        }
        int rowWidth = getWidth() / COLUMNS;
        for (int i = 0; i <= COLUMNS+1; i++) {
            double x = i * rowWidth - columnChange;
            g.drawLine((int)x, 0, (int)x, getHeight());
        }
        //Paint Grid
        /**
         * TO DO move grid as robot moves. Grid and Breadcrumbs should be
         * relative to each other, not to RobotIcon. It makes the user feel
         * like the robot is moving, not the Grid
         */
//            int rowHeight = getHeight() / ROWS;
//            for (int i = 0; i < ROWS; i++) {
//                g.drawLine(0, i * rowHeight , getWidth(), i * rowHeight);
//            }
//
//            int rowWidth = getWidth() / COLUMNS;
//            for (int i = 0; i < COLUMNS; i++) {
//                g.drawLine(i * rowWidth , 0, i * rowWidth , getHeight());
//            }
        //Paint History
        g.setColor(Color.GRAY);
        for (int i = 0; i < pointHistory.size() - 1; i++) {
            RealPoint p = pointHistory.get(i);
            RealPoint next = pointHistory.get(i + 1);
            RealPoint nextDisplay = toDisplayPoint(p);
            RealPoint display = toDisplayPoint(next);
            g.drawLine((int) nextDisplay.getX(), (int) nextDisplay.getY(),
                    (int) display.getX(), (int) display.getY());
            g.fillOval((int) display.getX() - CRUMB_POINT_SIZE / 2,
                    (int) display.getY() - CRUMB_POINT_SIZE / 2,
                    CRUMB_POINT_SIZE, CRUMB_POINT_SIZE);
        }
        if (select) {
            RealPoint p = toDisplayPoint(currentDestination);
            g.drawImage(CursorSelect, (int) p.getX() - (CursorSelect.getWidth(null) / 2),
                    (int) p.getY() - (CursorSelect.getHeight(null) / 2), null);
        }

        Graphics2D g2 = (Graphics2D) g.create();
        Image robot = RobotIcon.display();

        //Move the coordinate system to the absolute center of the screen
        g2.translate(this.getWidth() / 2.0, this.getHeight() / 2.0);

        //rotate it by the specified angle
        g2.rotate(Math.toRadians(model.getHeading()));

        //Draw Robot offset to account for drawing starting at upper left corner
        g2.drawImage(robot, -robot.getWidth(null) / 2,
                -robot.getHeight(null) / 2, this);

        if (commandWindowActive) {
            //Graphics2D g3 = (Graphics2D) g.create();
            g.drawImage(CommandWindow, commandWindowX
                    - CommandWindow.getWidth(null) / 2,
                    commandWindowY - CommandWindow.getHeight(null) / 2,
                    null);
            /*double angle = Math.atan2(lastY-commandWindowY,lastX-commandWindowX);
            double distance = Math.sqrt(Math.pow(lastY-commandWindowY, 2)+Math.pow(lastX-commandWindowX, 2));
            g3.translate(commandWindowX, commandWindowY);
            g3.rotate(angle);
            g3.drawImage(CommandSelector,-CommandSelector.getWidth(null)/2,
            -CommandSelector.getHeight(null)/2,this);*/
        }

    }

    /**
     * Activate and display the command menu for stopping the robot,
     * clearing the screen, firing the cannon, and toggling the arm.
     */
    private void activateCommandWindow() {
        commandWindowActive = true;
        repaint();
    }

    /**
     * Deactivate the commeand menu and stop displaying it.
     */
    private void deactivateCommandWindow() {
        commandWindowActive = false;
        repaint();
    }

    /**
     * Clears the history of points.
     */
    public void clear() {
        while (pointHistory.size() > 1) {
            pointHistory.remove(0);
        }
    }

    @Override
    public void mouseClicked(MouseEvent e) {
    }

    @Override
    public void mouseEntered(MouseEvent e) {
    }

    @Override
    public void mouseExited(MouseEvent e) {
    }

    /**
     * Commands to execute on mouse press actions:
     * Right-Click == open Command Window at cursor position
     */
    @Override
    public void mousePressed(MouseEvent e) {
        System.out.println("pressed");
        if (e.getButton() == MouseEvent.BUTTON3) {
            System.out.println(e.getX() + " " + e.getY());
            commandWindowX = e.getX();
            commandWindowY = e.getY();
            activateCommandWindow();
        }
    }

    /**
     * Commands to execute on mouse release actions:
     * Left-Click == Move to point of cursor on screen
     * Right-CLick == activate selected option from Command Window,
     * change color of RobotIcon:
     *              RIGHT == toggle arm, blue
     *              DOWN == stop, black
     *              LEFT == clear history, gray
     *              UP == fire cannon, red
     */
    @Override
    public void mouseReleased(MouseEvent e) {

        if (e.getButton() == MouseEvent.BUTTON1) { // Left Click
            model.stop();
            RealPoint p = toRealPoint(new RealPoint(e.getX(), e.getY()));
            moveModelTo(p);
            currentDestination = p;
            System.out.println("Moving to " + p);
            RobotIcon.setImage(RobotIcon.GREEN);
            select = true;
            cursorX = e.getX();
            cursorY = e.getY();
        }
        if (e.getButton() == MouseEvent.BUTTON3) { // Right Click
            deactivateCommandWindow();
            int x = e.getX();
            int y = e.getY();
            double angle = Math.toDegrees(Math.atan2(y - commandWindowY, x - commandWindowX));
            double distance = Math.sqrt(Math.pow(y - commandWindowY, 2) + Math.pow(x - commandWindowX, 2));
            if (!(distance > 60 && distance < 200)) {
                return;
            }
            //RIGHT == toggle arm, blue
            if (angle > -45 && angle < 45) {
                model.toggleArm();
                RobotIcon.setImage(RobotIcon.BLUE);
            } //DOWN == stop, black
            else if (angle > 45 && angle < 135) {
                model.stop();
                RobotIcon.setImage(RobotIcon.BLACK);
            } //LEFT == clear history, gray
            else if (angle > 135 || angle < -135) {
                clear();
                RobotIcon.setImage(RobotIcon.GRAY);
            } //UP == fire cannon, red
            else if (angle > -135 && angle < -45) {
                model.fire();
                RobotIcon.setImage(RobotIcon.RED);
            }

            deactivateCommandWindow();
        }

    }

    @Override
    public void mouseDragged(MouseEvent e) {
    }

    @Override
    public void mouseMoved(MouseEvent e) {
        if (commandWindowActive) {
            lastX = e.getX();
            lastY = e.getY();
            repaint();
        }
    }

    @Override
    public void stateChanged() {
        pointHistory.add(new RealPoint(model.getX(), model.getY()));

        this.columnChange += (model.getX() -
                 pointHistory.get(pointHistory.size()-2).getX())*DISTANCE_CONVERSION_FACTOR;
        /*if(columnChange > getWidth()/COLUMNS || columnChange< -getWidth()/COLUMNS){
            this.columnChange = 0;
        }*/
        columnChange %= ((double)getWidth())/((double)COLUMNS);
        
        this.rowChange +=  (model.getY() -
                        pointHistory.get(pointHistory.size()-2).getY())*DISTANCE_CONVERSION_FACTOR;
        /*if(rowChange > getHeight()/ROWS || rowChange< -getHeight()/ROWS){
            this.rowChange = 0;
        }*/
        rowChange %= ((double)getHeight())/((double)ROWS);
        repaint();
    }

    /**
     * Converts a virtual location on the screen to a real location in the world.
     * @param virtual The virtual point.
     * @return The real point.
     */
    private RealPoint toRealPoint(RealPoint virtual) {
        double x = virtual.getX();
        double y = virtual.getY();
        RealPoint lastPoint = pointHistory.get(pointHistory.size() - 1);
        x = ((x - (getWidth() / 2)) * DISTANCE_CONVERSION_FACTOR)
                + lastPoint.getX();
        y = ((y - (getHeight() / 2)) * -DISTANCE_CONVERSION_FACTOR)
                + lastPoint.getY();
        RealPoint p = new RealPoint(x, y);
        return p;
    }

    /**
     * Converts a real point in the world to a virtual point on the screen.
     * @param real The real point.
     * @return The virtual point.
     */
    private RealPoint toDisplayPoint(RealPoint real) {
        double x = real.getX();
        double y = real.getY();
        RealPoint lastPoint = pointHistory.get(pointHistory.size() - 1);
        x = ((x - lastPoint.getX()) / DISTANCE_CONVERSION_FACTOR)
                + (getWidth() / 2);
        y = ((y - lastPoint.getY()) / -DISTANCE_CONVERSION_FACTOR)
                + (getHeight() / 2);
        RealPoint p = new RealPoint(x, y);
        return p;
    }

    /**
     * Tells the model to go to a place in the world.
     * @param point The point to go to.
     */
    protected void moveModelTo(RealPoint point) {
        RealPoint currentPoint = pointHistory.get(pointHistory.size() - 1);
        model.moveTo(currentPoint.distanceTo(point), currentPoint.headingTo(point), MODEL_SPEED);
        // System.out.println(currentPoint.distanceTo(point) + " " +
        // currentPoint.headingTo(point));
    }

    @Override
    public void keyPressed(KeyEvent e) {
    }

    /**
     * Commands to execute when keys are released:
     * 'c' == clear history
     */
    @Override
    public void keyReleased(KeyEvent e) {
        switch (e.getKeyChar()) {
            case 'c':
                clear();
                break;
        }
    }
//yo man imma just trying to get commit to work

    @Override
    public void keyTyped(KeyEvent e) {
    }
}
